/*
GanttProject is an opensource project management tool.
Copyright (C) 2009-2011 Dmitry Barashev, GanttProject Team

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/
package org.ganttproject.impex.htmlpdf.itext;

import java.awt.BorderLayout;
import java.awt.Component;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;

import javax.swing.BorderFactory;
import javax.swing.JPanel;

import net.sourceforge.ganttproject.GPLogger;
import net.sourceforge.ganttproject.IGanttProject;
import net.sourceforge.ganttproject.export.AbstractExporter;
import net.sourceforge.ganttproject.export.ExportException;
import net.sourceforge.ganttproject.gui.UIFacade;
import net.sourceforge.ganttproject.gui.options.OptionsPageBuilder;
import net.sourceforge.ganttproject.gui.options.model.GPOptionGroup;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.ganttproject.impex.htmlpdf.AbstractEngine;
import org.ganttproject.impex.htmlpdf.ExporterToPDF;
import org.ganttproject.impex.htmlpdf.Stylesheet;
import org.ganttproject.impex.htmlpdf.StylesheetFactoryImpl;
import org.ganttproject.impex.htmlpdf.ExporterBase.ExporterJob;
import org.ganttproject.impex.htmlpdf.fonts.TTFontCache;
import org.osgi.service.prefs.Preferences;


public class ITextEngine extends AbstractEngine {
    private ITextStylesheet myStylesheet;
    private final TTFontCache myFontCache;
    private FontSubstitutionModel mySubstitutionModel;
    private Object myFontsMutex = new Object();
    private boolean myFontsReady = false;
    private ExporterToPDF myExporter;

    public ITextEngine(ExporterToPDF exporter) {
        myExporter = exporter;
        myFontCache = new TTFontCache();
        registerFonts();
    }

    public List<GPOptionGroup> getSecondaryOptions() {
        return Arrays.asList(getSecondaryOptionsArray());
    }

    private GPOptionGroup[] getSecondaryOptionsArray() {
        return ((ThemeImpl)myStylesheet).getOptions();
    }

    public Component getCustomOptionsUI() {
        waitRegisterFonts();
        JPanel result = new JPanel(new BorderLayout());
        result.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
        OptionsPageBuilder builder = new OptionsPageBuilder();

        List<GPOptionGroup> options = new ArrayList<GPOptionGroup>();
        options.addAll(myExporter.getSecondaryOptions());
        result.add(builder.buildPlanePage(options.toArray(new GPOptionGroup[0])), BorderLayout.NORTH);
        result.add(createFontPanel(), BorderLayout.CENTER);
        return result;
    }

    public String[] getCommandLineKeys() {
        return new String[] {"itext"};
    }

    private Component createFontPanel() {
        return new FontSubstitutionPanel(mySubstitutionModel).getComponent();
    }

    public void setContext(IGanttProject project, UIFacade uiFacade, Preferences preferences, Stylesheet stylesheet) {
        super.setContext(project, uiFacade, preferences);
        setSelectedStylesheet(stylesheet);
    }

    public void setSelectedStylesheet(Stylesheet stylesheet) {
        waitRegisterFonts();
        myStylesheet = (ITextStylesheet) stylesheet;
        if (getPreferences()!=null) {
            Preferences node = getPreferences().node("/configuration/org.ganttproject.impex.htmlpdf/font-substitution");
            mySubstitutionModel = new FontSubstitutionModel(myFontCache, myStylesheet, node);
            myStylesheet.setFontSubstitutionModel(mySubstitutionModel);
        }
    }

    public void setStylesheet(Stylesheet stylesheet) {
        myStylesheet = (ITextStylesheet) stylesheet;
    }

    public List<Stylesheet> getStylesheets() {
        StylesheetFactoryImpl factory = new StylesheetFactoryImpl() {
            @Override
            protected Stylesheet newStylesheet(URL resolvedUrl, String localizedName) {
                return new ThemeImpl(resolvedUrl, localizedName, getExporter(), myFontCache);
            }
        };
        return factory.createStylesheets(ITextStylesheet.class);
    }

    private AbstractExporter getExporter() {
        return myExporter;
    }

    private void registerFonts() {
        Thread fontReadingThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Random waiting seems silly, depending on the available
                    // resources (CPU speed, number of processes running etc)
                    // this might take longer or shorter...
                    // FIXME Add some better way of determining whether the fonts can be read already
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                registerFontDirectories();
                synchronized (ITextEngine.this.myFontsMutex) {
                    myFontsReady = true;
                    myFontsMutex.notifyAll();
                }
            }
        });
        fontReadingThread.setPriority(Thread.MIN_PRIORITY);
        fontReadingThread.start();
    }

    private void waitRegisterFonts() {
        while (myFontsMutex != null) {
            synchronized (myFontsMutex) {
                if (myFontsReady) {
                    break;
                }
                try {
                    myFontsMutex.wait();
                } catch (InterruptedException e) {
                    GPLogger.log(e);
                    break;
                }
            }
        }
    }

    protected void registerFontDirectories() {
        myFontCache.registerDirectory(System.getProperty("java.home") + "/lib/fonts");
        IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
        IConfigurationElement[] configElements =
            extensionRegistry.getConfigurationElementsFor("org.ganttproject.impex.htmlpdf.FontDirectory");
        for (int i=0; i<configElements.length; i++) {
            final String dirName = configElements[i].getAttribute("name");
            if (Boolean.TRUE.toString().equalsIgnoreCase(configElements[i].getAttribute("absolute"))) {
                myFontCache.registerDirectory(dirName);
            } else {
                String namespace = configElements[i].getDeclaringExtension().getNamespaceIdentifier();
                URL dirUrl = Platform.getBundle(namespace).getResource(dirName);
                if (dirUrl==null) {
                    GPLogger.getLogger(ITextEngine.class)
                        .warning("Failed to find directory " + dirName + " in plugin " + namespace);
                    continue;
                }
                try {
                    URL resolvedDir = Platform.resolve(dirUrl);
                    myFontCache.registerDirectory(resolvedDir.getPath());
                } catch (IOException e) {
                   GPLogger.getLogger(ITextEngine.class).log(Level.WARNING, e.getMessage(), e);
                   continue;
                }
            }
        }
    }

    public ExporterJob[] createJobs(File outputFile, List<File> resultFiles) {
        waitRegisterFonts();
        return new ExporterJob[] {createTransformationJob(outputFile)};
    }

    private ExporterJob createTransformationJob(final File outputFile) {
        ExporterJob result = new ExporterJob("Generating PDF") {
            @Override
            protected IStatus run() {
                assert myStylesheet!=null;
                OutputStream out = null;
                try {
                    out = new FileOutputStream(outputFile);
                    ((ThemeImpl)myStylesheet).run(getProject(), getUiFacade(), out);
                } catch (ExportException e) {
                    throw new RuntimeException(e);
                } catch (FileNotFoundException e) {
                    throw new RuntimeException(e);
                } finally {
                }
                return Status.OK_STATUS;
            }
        };
        return result;
    }
}
